package com.threegroup.web.service;

import com.alibaba.fastjson.JSON;
import com.threegroup.common.dto.ShoppingCartDTO;
import com.threegroup.common.exception.TakeOutException;
import com.threegroup.common.redis.RedisUtils;
import com.threegroup.common.utils.AssertUtil;
import com.threegroup.common.utils.Result;
import com.threegroup.common.validator.AssertUtils;
import com.threegroup.web.dao.ShoppingCartEntityMapper;
import com.threegroup.web.pojo.CategoryEntity;
import com.threegroup.web.pojo.ShoppingCartEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class ShoppingCartService {


    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ShoppingCartEntityMapper shoppingCartEntityMapper;

    public static Long TIME_OUT_CACHE = 60L;
    //添加购物车菜品的有效时间5分钟
    public static Long TIME_OUT_CACHE_SHOPPINGCART = 60 * 30l;

    public Result<List<ShoppingCartEntity>> getShoppingCartList(String id) {
        Result<List<ShoppingCartEntity>> listResult = new Result<>();

        /**
         * 准备redis模板
         */
        HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        //如果上次查到该id下的信息为空，就会缓存一个标记，如果监测到该标记，就不再查询数据库
        String aNull = opsForHash.get(id + "nu", "nu");
        if ("nu".equals(aNull)){
            //准备一个符合格式的空数据集并返回
            List<ShoppingCartEntity> shoppingCartEntityList = new ArrayList<>();
            listResult.setData(shoppingCartEntityList);
            return listResult;
        }

        //查询redis是否存在该信息，查到就返回结果
        List<ShoppingCartEntity> shoppingCartEntityList = new ArrayList<>();
        Map<String, String> entries = opsForHash.entries(id);//根据key获取所有的redis
        if (!entries.isEmpty()) {

            //遍历结果集
            for (String s1 : entries.keySet()) {
                String s = entries.get(s1);

                //获取每一个菜品信息
                ShoppingCartEntity shoppingCartEntity = JSON.parseObject(s, ShoppingCartEntity.class);

                //添加List集合
                if (shoppingCartEntity != null) {
                    shoppingCartEntityList.add(shoppingCartEntity);
                }
            }

            //如果查到的Redis里有数据，则直接返回
            listResult.setData(shoppingCartEntityList);


            //查询list时，把查询到的所有菜品同一为购物车，并同步到数据库
            int i = shoppingCartEntityMapper.copyDataToDB(shoppingCartEntityList);
            //返回结果
            return listResult;

        }

        //否则 查询数据库
        shoppingCartEntityList = shoppingCartEntityMapper.queryByUserId(Long.valueOf(id));

        //查询到结果集同步到redis缓存起来
        if (shoppingCartEntityList != null && shoppingCartEntityList.size() != 0) {

            //保存redis
            //把查到的结果转型为Map
            Map<String, String> map = new HashMap<>();
            //遍历list结果集
            for (ShoppingCartEntity shoppingCartEntity : shoppingCartEntityList) {
                //添加到map
                map.put(String.valueOf(shoppingCartEntity.getDishId()), JSON.toJSONString(shoppingCartEntity));
            }
            //添加redis
            opsForHash.putAll(id, map);
            //设置失效时间30分
            stringRedisTemplate.expire(id, TIME_OUT_CACHE, TimeUnit.SECONDS);

            //返回数据
            listResult.setData(shoppingCartEntityList);
            return listResult;
        }
        //没有查到，就存一个10秒的空标记到redis
        else {
            //为空，存一个空标记，防止重复查询
            opsForHash.put(id+"nu","nu","nu");
            //设定一个5秒的空缓存标记
            stringRedisTemplate.expire(id+"nu",10l,TimeUnit.SECONDS);
            //准备一个符合格式的空数据集并返回
            shoppingCartEntityList = new ArrayList<>();
            listResult.setData(shoppingCartEntityList);
            return listResult;
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Result<ShoppingCartDTO> addShoppingCart(String id, ShoppingCartDTO shoppingCartDTO) {
        //设置userId
        shoppingCartDTO.setUserId(Long.valueOf(id));
        //数据校验
        checkShoppingCartParams(shoppingCartDTO);
        //设置默认参数
        shoppingCartDTO.setCreator(shoppingCartDTO.getUserId());
        shoppingCartDTO.setCreateDate(new Date());
        shoppingCartDTO.setUpdater(shoppingCartDTO.getUserId());
        shoppingCartDTO.setUpdateDate(new Date());

        /**
         * 建立redis模板
         */
        HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        //用户一旦执行添加操作，就判断是否该用户存有空标记，存在就删除
        String aNull = opsForHash.get(id + "nu", "nu");

            if (aNull!=null&&"nu".equals(aNull)){
                stringRedisTemplate.expire(id + "nu",-2l,TimeUnit.SECONDS);
            }



        //查询请求是都存在redis
        String s = opsForHash.get(id, String.valueOf(shoppingCartDTO.getDishId()));
        if (!StringUtils.isBlank(s)) {//存在就取出数量
            //转换对象
            ShoppingCartDTO o = JSON.parseObject(s, ShoppingCartDTO.class);
            //把redis中对象的数量取出并存入传入的对象
            shoppingCartDTO.setNumber(o.getNumber());
            //同步总价
            shoppingCartDTO.setAmount(o.getAmount());
        }

        //是添加
        Integer number = shoppingCartDTO.getNumber();
        //number是空就置0，否则就等于本身，防止新添加的数据为空报错
        number = (number == null ? 0 : number);
        //在+1后赋值给传入的对象
        shoppingCartDTO.setNumber(++number);
        //计算amount价格
        //double amount = shoppingCartDTO.getAmount().doubleValue();
        //if (number == 1) {
        //    amount = amount * number;
        //} else if (number >= 2) {
        //    amount = amount / (number - 1) * number;
        //}
        ////回填价格
        //shoppingCartDTO.setAmount(BigDecimal.valueOf(amount));

        //设置返回结果集
        Result<ShoppingCartDTO> result = new Result<>();
        result.setData(shoppingCartDTO);

//        shoppingCartDTO.setAmount();
        //根据用户Id，以及菜品的dishid，添加传入的对象到redis
        String toJSONString = JSON.toJSONString(shoppingCartDTO);
        opsForHash.put(id, String.valueOf(shoppingCartDTO.getDishId()), toJSONString);
        //设置30分钟有效
        stringRedisTemplate.expire(id, TIME_OUT_CACHE_SHOPPINGCART, TimeUnit.SECONDS);

        return result;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Result<ShoppingCartDTO> subShoppingCart(String id, ShoppingCartDTO shoppingCartDTO) {
        //设置userId
        shoppingCartDTO.setUserId(Long.valueOf(id));
        //数据校验
        checkShoppingCartParams(shoppingCartDTO);
        //设置默认参数
        shoppingCartDTO.setUserId(Long.valueOf(id));
        shoppingCartDTO.setCreator(shoppingCartDTO.getUserId());
        shoppingCartDTO.setCreateDate(new Date());
        shoppingCartDTO.setUpdater(shoppingCartDTO.getUserId());
        shoppingCartDTO.setUpdateDate(new Date());
        /**
         * 建立redis模板
         */
        HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        //查询请求是都存在redis
        String s = opsForHash.get(id, String.valueOf(shoppingCartDTO.getDishId()));
        if (!StringUtils.isBlank(s)) {//存在就取出数量
            //转换对象
            ShoppingCartDTO o = JSON.parseObject(s, ShoppingCartDTO.class);
            //把redis中对象的数量取出并存入传入的对象
            shoppingCartDTO.setNumber(o.getNumber());
            //同步总价
            shoppingCartDTO.setAmount(o.getAmount());
        }

        //是减少
        Integer number = shoppingCartDTO.getNumber();
        //number是空就置0，否则就等于本身，防止新添加的数据为空报错
        number = (number == null ? 0 : number);
        //在-1后赋值给传入的对象
        AssertUtil.isTrue(number == 0, 303, "购物车中没有该菜品");
        shoppingCartDTO.setNumber(--number);

        ////计算amount价格
        //double amount = shoppingCartDTO.getAmount().doubleValue();
        //if (number == 0) {
        //    amount = 0;
        //} else if (number >= 1) {
        //    amount = amount / (number + 1) * number;
        //}
        ////回填价格
        //shoppingCartDTO.setAmount(BigDecimal.valueOf(amount));
        Result<ShoppingCartDTO> result = new Result<>();
        result.setData(shoppingCartDTO);
        //设置返回结果集
        //根据用户Id，以及菜品的dishid，添加传入的对象到redis
        String toJSONString = JSON.toJSONString(shoppingCartDTO);
        opsForHash.put(id, String.valueOf(shoppingCartDTO.getDishId()), toJSONString);
        //设置5分钟有效
        stringRedisTemplate.expire(id, TIME_OUT_CACHE_SHOPPINGCART, TimeUnit.SECONDS);
        //返回结果集
        return  result;
    }

    /**
     * 校验数据
     *
     * @param shoppingCartDTO
     */
    private void checkShoppingCartParams(ShoppingCartDTO shoppingCartDTO) {
        AssertUtils.isNull(shoppingCartDTO.getDishId(), 302, "菜品信息不存在");
        AssertUtils.isNull(shoppingCartDTO.getUserId(), 302, "用户信息不存在");
        AssertUtils.isBlank(shoppingCartDTO.getName(), 302, "菜品名不存在");
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteShopping(String id) {
        AssertUtils.isBlank(id, 302, "待删除信息不存在");
        /**
         * 建立redis模板
         */
        HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        Map<String, String> entries = opsForHash.entries(id);
        //redis有值就删除
        if (!(entries.isEmpty() && entries.size() == 0)) {

            //删除redis
            stringRedisTemplate.expire(id, -2l, TimeUnit.SECONDS);

        }

        //查询数据库，删除数据库中的值
        List<ShoppingCartEntity> shoppingCartEntityList = shoppingCartEntityMapper.queryByUserId(Long.valueOf(id));
        //判断是否有值，有就删除
        if (shoppingCartEntityList != null && shoppingCartEntityList.size() != 0) {
            AssertUtil.isTrue(shoppingCartEntityList.size() != shoppingCartEntityMapper.deleteByUserId(id), 303, "删除订单失败");
        }

    }
}
